#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#include "Log.hpp"
using namespace ns_log;
/*打印日志并exit程序*/

#define CREATE_SEM (IPC_CREAT | IPC_EXCL | 0666)
#define GET_SEM (IPC_CREAT)
#define P_OP (-1) /*P操作*/
#define V_OP (1)  /*V操作*/
const std::string SEM_PATH = "../tmp";
const int proj_id = 123;
const int default_sem_nums = 1;
/*信号量封装--P-V*/
std::string ToHex(int num)
{
    char buf[64];
    sprintf(buf, "0x%x", num);
    return buf;
}
class Semaphore
{
private:
    int _fd;

private:
    void PV(int op)
    {
        struct sembuf sem_buf;
        sem_buf.sem_num = 0;
        sem_buf.sem_op = op; // 本质是num + op
        sem_buf.sem_flg = SEM_UNDO;
        int n = semop(_fd, &sem_buf, 1);
        if (n < 0)
        {
            LOG(DEBUG, "op = %d失败\n", op);
            return;
        }
    }

public:
    Semaphore(int fd) : _fd(fd)
    {
    }
    void P()
    {
        PV(P_OP);
        LOG(DEBUG, "P操作done\n");
    }
    void V()
    {
        PV(V_OP);
        LOG(DEBUG, "V操作done\n");
    }
    /*释放过程交给信号量自身*/
    ~Semaphore()
    {
        if (_fd > 0)
        {
            int n = semctl(_fd, 0, IPC_RMID);
            if (n < 0)
            {
               LOG(ERROR, "semctl的IPC_RMID操作异常!,异常信息:%s\n", strerror(errno));
            }
            LOG(DEBUG, "销毁信号量done\n");
        }
    }
};
using SemPtr = std::shared_ptr<Semaphore>;

// 建造者模式辅助创建信号量
class SemaphoreBuilder
{
private:
    int _val;
private:
    bool Init(int fd, int num,int val)
    {
        union semun
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                      (Linux-specific) */
        } sem_un;
        sem_un.val = val;
        for (int i = 0; i < num; i++)
        {
            int ret = semctl(fd, i, SETVAL, sem_un);
            if (ret < 0)
            {
                LOG(ERROR, "semctl的SETVAL操作异常!,异常信息:%s\n", strerror(errno));
                return false;
            }
        }
        return true;
    }

public:
    SemaphoreBuilder()
    {}
    SemaphoreBuilder& SetVal(int val)
    {
        _val = val;
        return *this;
    }
    // 建造
    SemPtr Build(int flag, int num = default_sem_nums)
    {
        LOG(DEBUG, "开始build信号量\n");

        // 1.先ftok得到key
        int key = ftok(SEM_PATH.c_str(), proj_id);
        if (key == -1)
        {
            LOG(ERROR, "ftok操作异常!,异常信息:%s\n", strerror(errno));
            return nullptr;
        }
        LOG(INFO, "frok的key为:%s\n", ToHex(key).c_str());
        // 2.设置信号量集总量
        int sem_fd = semget(key, num, flag);
        if (sem_fd == -1)
        {
            LOG(ERROR, "semget操作异常!,异常信息:%s\n", strerror(errno));
            return nullptr;
        }
        // 初始化,也可以交给外部初始化
        if (flag == CREATE_SEM)
        {
            Init(sem_fd, num,_val);
        }
        return std::make_shared<Semaphore>(sem_fd);
    }
    ~SemaphoreBuilder()
    {
    }
};